package oss.ngocminh.fw.forum;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Scanner;
import java.util.regex.Pattern;

import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;

import oss.ngocminh.fw.data.Post;
import oss.ngocminh.fw.data.Thread;
import oss.ngocminh.fw.forum.VBulletinPageParser.Result;

public class VBulettin implements ForumAdapter {

	private boolean passwordEncypted = false;
	private boolean prettyUrl = true;
	private HttpClient httpClient = new DefaultHttpClient();
	private VBulletinPageParser pageParser = new VBulletinPageParser();
	
	public VBulettin() {
		super();
	}

	@Override
	public HttpClient getHttpClient() {
		return httpClient;
	}

	@Override
	public boolean login(String userName, String password, String baseUrl) throws IOException {
		String url = baseUrl + "/login.php?do=login";
		HttpPost httpPost = new HttpPost(url);
		List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
		nameValuePairs.add(new BasicNameValuePair("vb_login_username", userName));
		if (!passwordEncypted) {
			nameValuePairs.add(new BasicNameValuePair("vb_login_password", password));
			nameValuePairs.add(new BasicNameValuePair("vb_login_md5password", ""));
			nameValuePairs.add(new BasicNameValuePair("vb_login_md5password_utf", ""));
		} else {
			throw new UnsupportedOperationException("Password encrypting is not yet supported. Maybe next version?");
		}
		nameValuePairs.add(new BasicNameValuePair("cookieuser", "1"));
		nameValuePairs.add(new BasicNameValuePair("submit", "Login"));
		nameValuePairs.add(new BasicNameValuePair("s", ""));
		nameValuePairs.add(new BasicNameValuePair("do", "login"));
        httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
        HttpResponse response = httpClient.execute(httpPost);
        
        String charset = AdapterUtils.getCharset(response, "UTF-8");
        InputStream is = null;
        try {
        	is = response.getEntity().getContent();
			Scanner scanner = new Scanner(is, charset);
        	boolean loggedIn = scanner.findWithinHorizon(Pattern.quote(
        			"<meta http-equiv=\"Refresh\""), 0) != null;
			return loggedIn;
        } finally {
        	try {
        		if (is != null) {
        			is.close();
				}
        	} catch (IOException e) {
        		System.out.println(e);
			}
        }
	}

	@Override
	public void getPostsOfPage(Thread thread, int page,
			PostLoadingCallback callback) throws IOException, ParseException {
		Reader reader = null;
		try {
			reader = openPageStream(thread, page);
        	Result result = pageParser.parse(reader);
        	thread.setTitle(result.getThreadTitle());
        	thread.setPageCount(result.getPageCount());
        	if (!result.getPosts().isEmpty()) {
        		callback.postsLoaded(result.getPosts());
			}
		} finally {
        	try {
        		if (reader != null) {
        			reader.close();
				}
        	} catch (IOException e) {
        		System.out.println(e);
			}
        }		
	}

	@Override
	public void getPostsFromPage(Thread thread, int page,
			PostLoadingCallback callback) throws IOException, ParseException {
		while (true) {
			Reader reader = null;
			try {
				reader = openPageStream(thread, page);
	        	Result result = new VBulletinPageParser().parse(reader);
	        	thread.setPageCount(result.getPageCount());
	        	thread.setTitle(result.getThreadTitle());
	        	if (result.getPageCount() > page) {
					page++;
				} else {
					break;
				}
	        	if (!result.getPosts().isEmpty()) {
	        		callback.postsLoaded(result.getPosts());
				}
			} finally {
	        	try {
	        		if (reader != null) {
	        			reader.close();
					}
	        	} catch (IOException e) {
	        		System.out.println(e);
				}
	        }
		}
	}

	@Override
	public void getNewPosts(Thread thread, Date fromDate,
			PostLoadingCallback callback) throws IOException, ParseException {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void getAllPosts(Thread thread, PostLoadingCallback callback)
			throws IOException, ParseException {
		getPostsFromPage(thread, 1, callback);
	}

	@Override
	public List<Post> getPostsOfPage(Thread thread, int page) throws IOException, ParseException {
		RememberCallback callback = new RememberCallback();
		getPostsOfPage(thread, page, callback);
		return callback.getPosts();
	}

	@Override
	public List<Post> getPostsFromPage(Thread thread, int page)
			throws IOException, ParseException {
		RememberCallback callback = new RememberCallback();
		getPostsFromPage(thread, page, callback);
		return callback.getPosts();
	}

	@Override
	public List<Post> getNewPosts(Thread thread, Date fromDate) {
		throw new UnsupportedOperationException();
	}

	@Override
	public List<Post> getAllPosts(Thread thread) throws IOException, ParseException {
		return getPostsFromPage(thread, 1);
	}

	private Reader openPageStream(Thread thread, int page)
			throws IOException, ClientProtocolException {
		InputStream is;
		String url = prettyUrl ?
				thread.getBaseUrl() + "/showthread.php/" + thread.getThreadId() + "/page" + page :
				thread.getBaseUrl() + "/showthread.php?t=" + thread.getThreadId() + "&page=" + page;
		HttpResponse response = httpClient.execute(new HttpGet(url));
		is = response.getEntity().getContent();
		return new InputStreamReader(is, "UTF-8");
	}

	@Override
	public boolean submitPost(Post post, Thread thread) {
		// TODO Auto-generated method stub
		return false;
	}

	public boolean isPasswordEncypted() {
		return passwordEncypted;
	}

	public void setPasswordEncypted(boolean passwordEncypted) {
		this.passwordEncypted = passwordEncypted;
	}
	
}
